package com.idealighter.game.games.orderma.struct;

import com.google.common.collect.HashMultimap;

import com.idealighter.game.games.orderma.util.OderMaCalculator;
import com.idealighter.game.games.orderma.util.OderMaCardsAnalyzer;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 玩家的牌 .
 * 
 * @date 2016年8月23日 上午11:26:57
 *
 */
public class OderMaSeatCards {

  // 麻将最大id
  public static final int MJ_MAX_ID = 27;

  public final OderMaSeat seat;
  // 手上的万、筒、条
  public final int[][] mjs = new int[3][9];

  // 打出的的万、筒、条
  public final int[][] playedmjs = new int[3][9];

  // 手上的万、筒、条的数量
  private final int[] mjCounts = new int[3];
  // 碰过的牌标识
  private final boolean[][] pengFlgs = new boolean[3][9];
  // 杠过的牌标识
  private final boolean[][] gangFlgs = new boolean[3][9];
  // 缺
  public OderMaMjType missing;
  // 玩家最后摸的牌
  private int lastDrawMj;
  // 胡牌的麻将map，key：mj,val:翻数
  private final Map<Integer, Integer> huMjs = new HashMap<>();
  // 一轮放过胡的最大翻数(放了后除非高于已放炮翻数否则不能胡)
  public int passHuMaxMultiple = -1;

  // 正在杠的麻将
  public int gangIngMj = 0;


  public OderMaSeatCards(OderMaSeat seat) {
    this.seat = seat;
  }

  /**
   * 重置麻将数据.
   */
  public void reset() {
    for (int i = 0; i < mjs.length; i++) {
      Arrays.fill(mjs[i], 0);
    }

    for (int i = 0; i < playedmjs.length; i++) {
      Arrays.fill(playedmjs[i], 0);
    }

    Arrays.fill(mjCounts, 0);
    for (int i = 0; i < pengFlgs.length; i++) {
      Arrays.fill(pengFlgs[i], false);
    }
    for (int i = 0; i < gangFlgs.length; i++) {
      Arrays.fill(gangFlgs[i], false);
    }

    this.missing = null;
    this.lastDrawMj = 0;
    this.huMjs.clear();
    this.passHuMaxMultiple = -1;
    this.gangIngMj = 0;
  }

  /**
   * 增加麻将.
   * 
   * @param mj ..
   */
  public void addMj(OderMaMj mj) {
    addMj(mj.id);
  }

  /**
   * 增加麻将.
   * 
   * @param mjId ..
   */
  public void addMj(int mjId) {
    mjs[(mjId - 1) / 9][(mjId - 1) % 9]++;
    mjCounts[(mjId - 1) / 9]++;
    this.lastDrawMj = mjId;
  }

  /**
   * 增加麻将.
   * 
   * @param mjs .
   */
  public void addMjs(List<Integer> mjs) {
    for (int mj : mjs) {
      addMj(mj);
    }
  }



  /**
   * 记录打出的牌.
   * 
   * @param mjId ..
   */
  public void playedMj(int mjId) {
    playedmjs[(mjId - 1) / 9][(mjId - 1) % 9]++;
    // mjCounts[(mjId - 1) / 9]++;
  }

  /**
   * 摸牌,如果胡不了计算听牌信息.
   * 
   * @param mjId ..
   */
  public void drawMj(int mjId) {
    seat.table.preMianXiaGangMj = 0;
    seat.table.preMianXiaGangPlayer = 0;
    addMj(mjId);
  }

  public boolean[][] getPengFlag() {
    return pengFlgs;
  }

  /**
   * 计算听牌麻将,key:打出的麻将,val:胡牌的集合 . 注：摸牌、碰牌后计算，从当前牌中移除一张，再加入一张看是否胡牌.
   * 
   * @return
   */
  public HashMultimap<Integer, Map<Integer, Integer>> calcTingMjs() {
    HashMultimap<Integer, Map<Integer, Integer>> tingMjs = HashMultimap.create();
    for (int playMj = 1; playMj <= MJ_MAX_ID; playMj++) {
      if (contains(playMj)) {
        removeMj(playMj);
        for (int huMj = 1; huMj <= MJ_MAX_ID; huMj++) {
          if (huMj != playMj) {
            addMj(huMj);
            if (OderMaCardsAnalyzer.huPaiType(this) != null) {
              Map<Integer, Integer> huMap = new HashMap<>();
              int multi = OderMaCalculator.calcHuMultiple(this, OderMaHuType.PING_HU,
                  OderMaCardsAnalyzer.huPaiType(this), huMj);
              huMap.put(huMj, multi);
              tingMjs.put(playMj, huMap);
            }
            removeMj(huMj);
          }
        }
        addMj(playMj);
      }
    }
    return tingMjs;
  }

  /**
   * 移除麻将后从听牌信息中获取保存胡牌信息.
   * 
   * @param mj ..
   * @return 移除麻将.
   */
  public boolean removeMj(OderMaMj mj) {
    return removeMj(mj.id);
  }

  /**
   * 移除麻将.
   * 
   * @param mjId .
   * @return
   */
  public boolean removeMj(int mjId) {
    if (contains(mjId)) {
      mjs[(mjId - 1) / 9][(mjId - 1) % 9]--;
      mjCounts[(mjId - 1) / 9]--;
      return true;
    }

    return false;
  }

  /**
   * 移除麻将 .
   * 
   * @param mjIds .
   */
  public void removeMjs(List<Integer> mjIds) {
    for (Integer mjId : mjIds) {
      removeMj(mjId);
    }
  }

  /**
   * 出牌 .
   * 
   * @param mjId .
   */
  public void playMj(int mjId) {
    final OderMaTable table = seat.table;
    removeMj(mjId);
    playedMj(mjId);
    calcHuMjs();

    /*
     * 出牌后重置上一个杠牌玩家 .
     */
    if (seat != table.preGangedSeat) {
      table.preGangedSeat = null;
    }
  }

  /**
   * 计算胡牌麻将.
   */
  public void calcHuMjs() {
    huMjs.clear();
    for (int huMj = 1; huMj <= MJ_MAX_ID; huMj++) {
      addMj(huMj);
      OderMaHuPaiType huPaiType;
      if ((huPaiType = OderMaCardsAnalyzer.huPaiType(this)) != null) {
        huMjs.put(huMj,
            OderMaCalculator.calcHuMultiple(this, OderMaHuType.PING_HU, huPaiType, huMj));
      }
      removeMj(huMj);
    }
  }

  /**
   * 是否含有麻将 .
   * 
   * @param mjId .
   * @return
   */
  public boolean contains(int mjId) {

    return mjs[(mjId - 1) / 9][(mjId - 1) % 9] > 0;
  }

  /**
   * 是否含有麻将 .
   * 
   * @param mjIds .
   * @return
   */
  public boolean contains(List<Integer> mjIds) {
    for (int i = 0; i < mjIds.size(); i++) {
      if (!contains(mjIds.get(i))) {
        return false;
      }
    }

    return true;
  }

  /**
   * 该麻将是否缺的类型 .
   * 
   * @param mjId .
   * @return
   */
  public boolean missing(int mjId) {

    return (mjId - 1) / 9 == missing.index;
  }

  /**
   * 是否可以碰,有至少2张且不是缺 .
   * 
   * @param mjId .
   * @return
   */
  public boolean pengAble(int mjId) {
    int typeIndex = (mjId - 1) / 9;
    return mjs[typeIndex][(mjId - 1) % 9] > 1 && typeIndex != missing.index;
  }

  /**
   * 是否可以杠,手上有至少3张或碰过且还有牌可以摸且不是缺 .
   * 
   * @param mjId .
   * @return
   */
  public boolean gangAble(int mjId) {
    int typeIndex = (mjId - 1) / 9;
    return gangType(mjId) != null && seat.table.hasMj() && typeIndex != missing.index;
  }

  /**
   * 是否可以胡,放炮后除非高于已放炮翻数否则不能胡,默认是平胡类型 .
   * 
   * @param mjId .
   * @return
   */
  /*
   * public . boolean huAble(int mjId) { // 牌型的翻数 Integer huMultiple = huMjs.get(mjId); return
   * huMultiple . != null && huMultiple > passHuMaxMultiple; }
   */

  /**
   * 是否可以胡,放炮后除非高于已放炮翻数否则不能胡 .
   * 
   * @param mjId .
   * @param huType .
   * @return
   */
  /*
   * public . boolean huAble(int mjId, OderMaHuType huType) { // 牌型的翻数 Integer huMultiple =
   * huMjs.get(mjId); return huMultiple != null && huMultiple + huType.multiple() -
   * OderMaHuType.PING_HU.multiple() . > passHuMaxMultiple; }
   */

  /**
   * .
   * 
   * @param mjId .
   * @param isZiMo .
   * @return
   */
  public boolean huAble(int mjId, boolean isZiMo) {
    // 牌型的平胡翻数
    Integer huPaiMultiple = huMjs.get(mjId);

    if (huPaiMultiple != null) {
      if (isZiMo || passHuMaxMultiple == -1) {
        return true;
      } else if (passHuMaxMultiple > -1) {
        OderMaHuType huType = OderMaCardsAnalyzer.huType(this, mjId, isZiMo);
        return huType.multiple() + huPaiMultiple
            - OderMaHuType.PING_HU.multiple() > passHuMaxMultiple;
      }
    }

    return false;
  }

  /**
   * 计算杠的类型 .
   * 
   * @param mjId .
   * @return
   */
  public OderMaGangType gangType(int mjId) {
    // 类型索引
    int typeIndex = (mjId - 1) / 9;
    // 数字索引
    int numIndex = (mjId - 1) % 9;

    if (getTotalCount() % 3 == 2) {
      // 该麻将数量
      int mjCount = mjs[typeIndex][numIndex];
      if (mjCount == 4) {
        return OderMaGangType.AN_GANG;
      } else if (pengFlgs[typeIndex][numIndex] && mjCount == 1) {
        return OderMaGangType.MIAN_XIA_GANG;
      }
    } else if (mjs[typeIndex][numIndex] == 3) {
      return OderMaGangType.ZHI_GANG;
    }

    return null;
  }

  /**
   * 碰 .
   * 
   * @param mjId .
   */
  public void peng(int mjId) {
    removeMj(mjId);
    removeMj(mjId);
    pengFlgs[(mjId - 1) / 9][(mjId - 1) % 9] = true;
  }

  /**
   * 杠 .
   * 
   * @param mjId .
   */
  public void gang(int mjId, OderMaGangType gangType) {
    // 类型索引
    int typeIndex = (mjId - 1) / 9;
    // 数字索引
    int numIndex = (mjId - 1) % 9;
    switch (gangType) {
      case AN_GANG:
        removeMj(mjId);
        removeMj(mjId);
        removeMj(mjId);
        removeMj(mjId);
        break;
      case MIAN_XIA_GANG:
        removeMj(mjId);
        pengFlgs[typeIndex][numIndex] = false;
        break;
      case ZHI_GANG:
        removeMj(mjId);
        removeMj(mjId);
        removeMj(mjId);
        break;
      default:
        break;
    }

    gangFlgs[typeIndex][numIndex] = true;
    seat.table.preGangedSeat = seat;
    calcHuMjs();
  }

  /**
   * 麻将List .
   * 
   * @return
   */
  public List<Integer> mjList() {
    List<Integer> mjList = new ArrayList<>();
    for (int typeIndex = 0; typeIndex < mjs.length; typeIndex++) {
      int[] typeMjs = mjs[typeIndex];
      for (int numIndex = 0; numIndex < typeMjs.length; numIndex++) {
        int count = typeMjs[numIndex];
        for (int k = 0; k < count; k++) {
          mjList.add(typeIndex * 9 + numIndex + 1);
        }
      }
    }

    return mjList;
  }

  /**
   * 最少麻将类型 .
   * 
   * @return
   */
  public OderMaMjType minMjType() {
    int wanCount = getWanCount();
    int tongCount = getTongCount();
    int tiaoCount = getTiaoCount();

    if (wanCount <= tongCount && wanCount <= tiaoCount) {
      return OderMaMjType.WAN;
    } else if (tongCount <= wanCount && tongCount <= tiaoCount) {
      return OderMaMjType.TONG;
    } else if (tiaoCount <= tongCount && tiaoCount <= wanCount) {
      return OderMaMjType.TIAO;
    }

    return OderMaMjType.WAN;
  }

  /**
   * 计算自动出牌的麻将,先出缺，再出最后摸的牌 .
   * 
   * @return 麻将id
   */
  public int autoPlayMj() {
    // 缺牌的合计数量
    int missingCount = getCount(missing);
    if (missingCount > 0) {
      int[] typeMjs = mjs[missing.index];
      for (int numIndex = 0; numIndex < typeMjs.length; numIndex++) {
        int count = typeMjs[numIndex];
        if (count > 0) {
          return missing.index * 9 + numIndex + 1;
        }
      }
    }

    return lastDrawMj;
  }

  /**
   * 复制麻将 .
   * 
   * @return
   */
  public int[][] copyMjs() {
    int len = mjs.length;
    int[][] copy = new int[len][];
    // 复制麻将,计算过程会改变值，先复制再计算
    for (int typeIndex = 0; typeIndex < len; typeIndex++) {
      copy[typeIndex] = Arrays.copyOf(mjs[typeIndex], mjs[typeIndex].length);
    }

    return copy;
  }

  /**
   * 是否清一色 .
   * 
   * @return
   */
  public boolean isQingYiSe() {
    boolean wanFlg = false;
    boolean tongFlg = false;
    boolean tiaoFlg = false;

    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;

      if (mjs[typeIndex][numIndex] > 0 || pengFlgs[typeIndex][numIndex]
          || gangFlgs[typeIndex][numIndex]) {
        if (typeIndex == 0) {
          wanFlg = true;
        } else if (typeIndex == 1) {
          tongFlg = true;
        } else if (typeIndex == 2) {
          tiaoFlg = true;
        }
      }
    }

    return (wanFlg && !tongFlg && !tiaoFlg) || (!wanFlg && tongFlg && !tiaoFlg)
        || (!wanFlg && !tongFlg && tiaoFlg);
  }

  /**
   * 断九幺 .
   * 
   * 
   * @return
   */
  public boolean isDuanjiuYao() {
    boolean flag = true;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;

      if (mjs[typeIndex][numIndex] > 0 || pengFlgs[typeIndex][numIndex]
          || gangFlgs[typeIndex][numIndex]) {
        if (numIndex == 0 || numIndex == 8) {
          flag = false;
        }
      }
    }

    return flag;
  }

  /**
   * 一色二顺 .
   * 
   * 
   * @return
   */
  public boolean isYiSeErShun() {
    int lianxuNum = 0;
    int tindex = 0;
    int nindex = 0;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;
      if (mjs[typeIndex][numIndex] == 2) {
        int tempIndex = tindex;
        int tempnIndex = nindex;
        tindex = typeIndex;
        nindex = numIndex;
        if (tempIndex == typeIndex && nindex == tempnIndex + 1) {
          lianxuNum++;
        }
      }
    }
    return lianxuNum >= 3 ? true : false;
  }
  //
  // @Test
  //
  // public void run() {
  // mjs[0][1] = 2;
  // mjs[0][2] = 2;
  // mjs[0][3] = 2;
  // mjs[1][4] = 2;
  // mjs[2][4] = 2;
  // mjs[2][1] = 2;
  // assertEquals("true", isDuanjiuYao());
  //
  // }


  /**
   * 胡坎张 .
   * 
   * 
   * @return
   */
  public boolean isHuKan(int mjIds) {


    if ((mjIds - 1) % 9 == 8 || (mjIds - 1) % 9 == 0) {
      return false;
    }
    // 复制麻将,先将最后胡的麻将移除，看看手里面有没有该麻将
    int[][] copy = copyMjs();

    if (copy[(mjIds - 1) / 9][(mjIds - 1) % 9] > 0) {
      copy[(mjIds - 1) / 9][(mjIds - 1) % 9]--;
    }

    boolean flag = copy[(mjIds - 1) / 9][(mjIds - 1) % 9] > 0;

    return huMjs.size() == 1 && !flag;

    // TODO

    // return false;
  }

  /**
   * 门清 .
   * 
   * @param isZm .
   * 
   * 
   * @return
   */
  public boolean isMenQing(boolean isZm) {
    boolean flag = true;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;
      if (pengFlgs[typeIndex][numIndex] || gangFlgs[typeIndex][numIndex]) {
        flag = false;
      }
    }
    return flag && !isZm;
  }

  /**
   * 胡单吊.
   * 
   * @param hmjId .
   * 
   * 
   * @return
   */
  public boolean isHuDanDiao(int hmjId) {

    int[][] copy = copyMjs();

    if (copy[(hmjId - 1) / 9][(hmjId - 1) % 9] > 0) {
      copy[(hmjId - 1) / 9][(hmjId - 1) % 9]--;
    }
    boolean flag = true;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;

      if (copy[typeIndex][numIndex] == 2) {
        flag = false;
      }
    }
    return flag;


    // if (!isEnd) {
    // return huMjs.size() == 1 && !contains(mjIds);
    // }
    //
    // int[][] copy = copyMjs();
    //
    // if (copy[(mjIds - 1) / 9][(mjIds - 1) % 9] > 0) {
    // copy[(mjIds - 1) / 9][(mjIds - 1) % 9]--;
    // }
    //
    // boolean flag = copy[(mjIds - 1) / 9][(mjIds - 1) % 9] > 0;
    //
    // // TODO
    // return huMjs.size() == 1 && flag;
  }

  /**
   * 一杠 .
   * 
   * 
   * @return
   */
  public int isYiGang() {
    int amount = 0;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;
      if (gangFlgs[typeIndex][numIndex] || mjs[typeIndex][numIndex] == 4) {
        amount++;
      }
    }
    return amount;
  }

  /**
   * 一起通贯 .
   * 
   * 
   * @return
   */
  public boolean isYiGuanTong() {
    int wanNum = 0;
    int tiaoNum = 0;
    int tongNum = 0;
    int wn = 0;

    int tiaoN = 0;
    int tongN = 0;
    for (int mjId = 1; mjId <= MJ_MAX_ID; mjId++) {
      // 类型索引
      int typeIndex = (mjId - 1) / 9;
      // 数字索引
      int numIndex = (mjId - 1) % 9;
      if (mjs[typeIndex][numIndex] > 0 || pengFlgs[typeIndex][numIndex]
          || gangFlgs[typeIndex][numIndex]) {
        if (typeIndex == 0) {
          int tmp = wn;
          wn = numIndex;
          if (numIndex == tmp + 1) {
            wanNum++;
          }
        } else if (typeIndex == 1) {
          int tmp = tiaoN;
          tiaoN = numIndex;
          if (numIndex == tmp + 1) {
            tiaoNum++;
          }

        } else if (typeIndex == 2) {
          int tmp = tongN;
          tongN = numIndex;
          if (numIndex == tmp + 1) {
            tongNum++;
          }
        }
      }
    }
    return wanNum >= 8 || tiaoNum >= 8 || tongNum >= 8;
  }

  /**
   * 绝支压裆 .
   * 
   * 
   * @return
   */
  public boolean isJueZhiYaDang(int mjid) {
    // TODO

    return seat.table.isLastOne(mjid) && isHuKan(mjid);
  }

  /**
   * 四合数量 .
   * 
   * @return
   */
  public int siHeCount(OderMaHuType huType, OderMaHuPaiType huPaiType) {
    int siHeCount = 0;
    for (int mj = 1; mj <= MJ_MAX_ID; mj++) {
      // 类型索引
      int typeIndex = (mj - 1) / 9;
      // 数字索引
      int numIndex = (mj - 1) % 9;
      if (mjs[typeIndex][numIndex] == 4) {
        // if ((contains(mj + 1) && contains(mj - 1)) || (contains(mj + 1) && contains(mj + 2))
        // || contains(mj - 1) && contains(mj - 2)) {
        siHeCount++;
        // }
      }
    }

    if (huType == OderMaHuType.GANG_SHANG_HUA) {
      return siHeCount - 1;
    }

    return siHeCount;
  }

  /**
   * 是否花猪 .
   * 
   * @return
   */
  public boolean isHuaZhu() {

    return getCount(missing) != 0;
  }

  /**
   * 是否无听 .
   * 
   * @return
   */
  public boolean isWuTing() {
    return huMjs.size() == 0;
  }

  /**
   * 有听时最大胡牌翻数 .
   * 
   * @return
   */
  public int maxHuPaiMultiple() {
    // 胡牌最大翻数
    int maxMultiple = 0;
    for (Integer multiple : huMjs.values()) {
      if (multiple > maxMultiple) {
        maxMultiple = multiple;
      }
    }

    return maxMultiple;
  }

  private int getCount(OderMaMjType mjType) {
    return getCount(mjType.index);
  }

  private int getCount(int typeIndex) {
    return mjCounts[typeIndex];
  }

  /**
   * 推荐定缺 .
   * 
   * 
   * @return
   */
  public OderMaMjType reCommandMissing() {
    int min = Math.min(Math.min(getWanCount(), getTongCount()), getTiaoCount());
    if (min == getWanCount()) {
      return OderMaMjType.WAN;
    } else if (min == getTongCount()) {
      return OderMaMjType.TONG;
    } else {
      return OderMaMjType.TIAO;
    }

  }

  /**
   * 获取手上万的数量 .
   * 
   * @return
   */
  public int getWanCount() {
    return getCount(OderMaMjType.WAN);
  }

  /**
   * 手上麻将的合计数量 .
   * 
   * @return
   */
  public int getTotalCount() {
    return mjCounts[0] + mjCounts[1] + mjCounts[2];
  }

  /**
   * 获取手上筒的数量 .
   * 
   * @return
   */
  public int getTongCount() {
    return getCount(OderMaMjType.TONG);
  }

  /**
   * 获取手上条的数量 .
   * 
   * @return
   */
  public int getTiaoCount() {
    return getCount(OderMaMjType.TIAO);
  }

  /**
   * 获取缺的数量 .
   * 
   * @return
   */
  public int getMissingCount() {
    return getCount(missing);
  }

  public int getLastDrawMj() {
    return lastDrawMj;
  }

  public Map<Integer, Integer> getHuMjs() {
    return huMjs;
  }

}
